package com.authsphere.plugin;

import com.authsphere.common.annotation.CryptField;
import com.authsphere.common.entity.PluginData;
import com.authsphere.common.enums.AlgorithmType;
import com.authsphere.common.enums.ParamType;
import com.authsphere.common.enums.PluginBeforeEnum;
import com.authsphere.plugin.api.AuthSphereBeforePluginChain;
import com.authsphere.plugin.api.WebExchange;
import com.authsphere.plugin.base.AbstractAuthSphereBeforePlugin;
import com.authsphere.plugin.base.cache.AnnotationDataCache;
import com.authsphere.plugin.base.entity.AnnotationFieldBean;
import com.authsphere.plugin.config.DecryptConfig;
import com.authsphere.plugin.decrypt.RequestBodyDecoder;
import com.authsphere.plugin.decrypt.RequestBodyDecoderFactory;
import com.authsphere.plugin.entity.CookieCryptFieldBean;
import com.authsphere.plugin.entity.CryptFieldBean;
import com.authsphere.plugin.entity.ObjectCryptFieldBean;
import com.authsphere.plugin.entity.ParameterCryptFieldBean;
import com.authsphere.plugin.exception.DecryptAuthenticationException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Triple;

import java.lang.reflect.Method;
import java.util.Optional;

/**
 * @program: AuthSphere
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/27 21:56
 **/
public class DecryptBeforePlugin extends AbstractAuthSphereBeforePlugin {

    private final DecryptConfig decryptConfig;

    public DecryptBeforePlugin(DecryptConfig decryptConfig) {
        this.decryptConfig = decryptConfig;
    }

    @Override
    public int getOrder() {
        return PluginBeforeEnum.DECRYPT.getCode();
    }

    @Override
    protected void doExecute(WebExchange exchange, AuthSphereBeforePluginChain chain, PluginData pluginData) {
        Triple<AlgorithmType, String, CryptFieldBean> triple = extractCryptValue(exchange);
        if (triple == null) {
            throw new DecryptAuthenticationException("参数不能为空");
        }
        CryptFieldBean cryptFieldBean = triple.getRight();
        if (cryptFieldBean == null) {
            throw new DecryptAuthenticationException("参数不能为空");
        }
        String fieldValue = cryptFieldBean.getFieldValue();
        if (StringUtils.isBlank(fieldValue)) {
            throw new DecryptAuthenticationException("参数不能为空");
        }
        AlgorithmType algorithmType = Optional.ofNullable(triple.getLeft()).orElse(AlgorithmType.AES);
        RequestBodyDecoder requestBodyDecoder = RequestBodyDecoderFactory.getInstance(algorithmType);
        String secretKey = triple.getMiddle();
        String decryptValue;
        if (StringUtils.isBlank(secretKey)) {
            decryptValue = requestBodyDecoder.decrypt(fieldValue);
        } else {
            decryptValue = requestBodyDecoder.decrypt(fieldValue, secretKey);
        }
        if (StringUtils.isBlank(decryptValue)) {
            throw new DecryptAuthenticationException("参数异常");
        }
        if (AlgorithmType.NONE != algorithmType && StringUtils.equals(decryptValue, fieldValue)) {
            throw new DecryptAuthenticationException("参数异常");
        }
        // 设置新value
        cryptFieldBean.setFieldValue(decryptValue);
        chain.execute(exchange);
    }

    private Triple<AlgorithmType, String, CryptFieldBean> extractCryptValue(WebExchange exchange) {
        ParamType paramType = decryptConfig.getParamType();
        return switch (paramType) {
            case BODY -> getAnnotatedValue(exchange);
            case PARAM -> getValueFromParameter(exchange);
            case HEADER -> getValueFromHeader(exchange);
            case COOKIE -> getValueFromCookie(exchange);
        };
    }

    private Triple<AlgorithmType, String, CryptFieldBean> getValueFromCookie(WebExchange exchange) {
        Cookie[] cookies = exchange.getRequest().getCookies();
        AlgorithmType algorithmType = AlgorithmType.NONE;
        String secretKey = "";
        String value = "";
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(decryptConfig.getParamName())) {
                    value = cookie.getValue();
                } else if (cookie.getName().equals(decryptConfig.getAlgorithmTypeName())) {
                    algorithmType = AlgorithmType.getAlgorithmType(cookie.getValue());
                } else if (cookie.getName().equals(decryptConfig.getSecretKeyName())) {
                    secretKey = cookie.getValue();
                }
            }
        }
        return ImmutableTriple.of(algorithmType, secretKey, new CookieCryptFieldBean(value, decryptConfig.getParamName(), exchange.getRequest(), exchange::setRequest));
    }

    private Triple<AlgorithmType, String, CryptFieldBean> getValueFromHeader(WebExchange exchange) {
        HttpServletRequest request = exchange.getRequest();
        String header = request.getHeader(decryptConfig.getParamName());
        CryptFieldBean cryptFieldBean = new ParameterCryptFieldBean(header, decryptConfig.getParamName(), exchange.getRequest(), exchange::setRequest);
        return ImmutableTriple.of(
                AlgorithmType.getAlgorithmType(request.getHeader(decryptConfig.getAlgorithmTypeName())),
                request.getHeader(decryptConfig.getSecretKeyName()),
                cryptFieldBean
        );
    }

    private Triple<AlgorithmType, String, CryptFieldBean> getValueFromParameter(WebExchange exchange) {
        HttpServletRequest request = exchange.getRequest();
        String parameterValue = request.getParameter(decryptConfig.getParamName());
        CryptFieldBean cryptFieldBean = new ParameterCryptFieldBean(parameterValue, decryptConfig.getParamName(), exchange.getRequest(), exchange::setRequest);
        return ImmutableTriple.of(
                AlgorithmType.getAlgorithmType(request.getParameter(decryptConfig.getAlgorithmTypeName())),
                request.getParameter(decryptConfig.getSecretKeyName()),
                cryptFieldBean
        );
    }

    private static Triple<AlgorithmType, String, CryptFieldBean> getAnnotatedValue(WebExchange exchange) {
        Object requestBody = exchange.getRequestBody();
        if (requestBody == null) {
            return ImmutableTriple.nullTriple();
        }
        AnnotationFieldBean<CryptField> annotationData = AnnotationDataCache.getAnnotationData(requestBody.getClass(), CryptField.class);
        if (annotationData == null) {
            throw new DecryptAuthenticationException("数据异常");
        }
        CryptField cryptField = annotationData.getAnnotation();
        if (cryptField == null) {
            return ImmutableTriple.nullTriple();
        }
        Method getterMethod = annotationData.getGetterMethod();
        Method setterMethod = annotationData.getSetterMethod();
        CryptFieldBean cryptFieldBean = new ObjectCryptFieldBean(requestBody, getterMethod, setterMethod, annotationData.getField());
        return ImmutableTriple.of(cryptField.algorithmType(), cryptField.secretKey(), cryptFieldBean);
    }

    @Override
    public String name() {
        return PluginBeforeEnum.DECRYPT.getName();
    }
}
